#include "../../../include/graphics/gl/GLTexture.hpp"
#include "../../../include/tools/General.hpp"
using namespace klas;
using namespace graphics;
using namespace gl;

GLTexture::GLTexture()
{
  zero();
}

GLTexture::GLTexture(string path)
{
  zero(); // Zero params
  load(path);   // Load the image.
}

GLTexture::~GLTexture()
{
  kill();
}

void GLTexture::zero()
{
  m_texID = 0;
  m_isValid = false;
}

void GLTexture::kill()
{
  if(m_isValid && m_texID != 0 && glIsTexture(m_texID))
    glDeleteTextures(1, &m_texID);

  // Call ctor to zero params.
  zero();
}

bool GLTexture::isValid() const
{
  return m_isValid;
}

bool GLTexture::load(string path)
{
  // kill data first if necessary
  kill();

  ILboolean       success;
  ILuint          ilTexID;

  // bind our texture
  ilGenImages(1, &ilTexID);
  ilBindImage(ilTexID);

  // attempt to load the image
  success = ilLoadImage(path.c_str());

  // if we did not load the image
  if (!success)
  {
    string what;
    what.append("GLTexture::load exception\n");

    ILenum Error;
    while ((Error = ilGetError()) != IL_NO_ERROR) {
      what.append(tools::General::toString(Error));
      what.append(": ");
      what.append(iluErrorString(Error));
      what.append("\n");
    }

    throw error::Exception(what);
  }

  // convert to RGBA
  success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);

  // if that failed? go to RGB
  if (!success)
    success = ilConvertImage(IL_RGB, IL_UNSIGNED_BYTE);

  m_originalDim.x = ilGetInteger(IL_IMAGE_WIDTH);
  m_originalDim.y = ilGetInteger(IL_IMAGE_HEIGHT);

  // convert to a power of two texture
  iluImageParameter(ILU_PLACEMENT, ILU_UPPER_LEFT);
  iluEnlargeCanvas(tools::General::getNextPowerOfTwo(ilGetInteger(IL_IMAGE_WIDTH)),
           tools::General::getNextPowerOfTwo(ilGetInteger(IL_IMAGE_HEIGHT)), 
           ilGetInteger(IL_IMAGE_BPP));

  if(!success)
    throw error::Exception("Texture::load was unable to convert the image to RGB(A)");

  // Have OpenGL generate a texture object handle for us
  glGenTextures( 1, &m_texID );

  // Bind the texture object
  glBindTexture( GL_TEXTURE_2D, m_texID );

  // Set the texture's stretching properties
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

  // Edit the texture object's image data using the information DevIL gives us
  glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), 
          ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 0, 
          ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());

  m_paddedDim.x = ilGetInteger(IL_IMAGE_WIDTH);
  m_paddedDim.y = ilGetInteger(IL_IMAGE_HEIGHT);

  // Because we have already copied image data into texture data we can release 
  // memory used by DevIL.
  ilDeleteImages(1, &ilTexID); 

  return true;
}

void GLTexture::activate()
{
  glBindTexture(GL_TEXTURE_2D, m_texID);
}

math::Vector2d GLTexture::getPaddedDim() const
{
  return m_paddedDim;
}